home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
fsconsist
/
fsconsistCache.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-18
|
77KB
|
2,329 lines
/*
* fsCacheConsist.c --
*
* Routines used to keep the file system caches consistent. The
* server maintains a list of client machines that have the file
* open. This list indicates how many opens per client, if the file
* is being cached, and if the file is open for writing on a client.
* The client list is updated when files are opened, closed, and removed.
*
* SYNCHRONIZATION: There are two classes of procedures here: those
* that make call-backs to clients, and those that just examine the
* client list. All access to a client list is synchronized using
* a monitor lock embedded in the consist structure. Furthermore,
* consistency actions are serialized by setting a flag during
* call-backs (CONSIST_IN_PROGRESS). There is a possible deadlock
* if the monitor lock is held during a call-back because this
* prevents a close operation from completing. (At close time the
* client list is adjusted but no call-backs are made. Also, the client
* has its handle locked which blocks our call-back.) Accordingly,
* both the handle lock and the monitor lock are released during
* a call-back. The CONSIST_IN_PROGRESS flag is left on to prevent
* other consistency actions during the call-back.
*
* Copyright 1986 Regents of the University of California.
* All rights reserved.
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/fsconsist/fsconsistCache.c,v 9.32 92/08/10 17:23:58 mgbaker Exp $ SPRITE (Berkeley)";
#endif
#include <sprite.h>
#include <fs.h>
#include <fsutil.h>
#include <fsconsist.h>
#include <fscache.h>
#include <fsStat.h>
#include <fslcl.h>
#include <fsprefix.h>
#include <hash.h>
#include <vm.h>
#include <proc.h>
#include <sys.h>
#include <rpc.h>
#include <recov.h>
#include <timer.h>
#include <dbg.h>
#include <fsio.h>
#include <fsrmt.h>
#include <fsdm.h>
#include <stdio.h>
#include <fsrecov.h>
#define LOCKPTR (&consistPtr->lock)
Boolean fsconsist_Debug = FALSE;
Boolean fsconsist_ClientCachingEnabled = TRUE;
/*
* Flags for the Fsconsist_Info struct that's defined in fsInt.h
*
* FS_CONSIST_IN_PROGRESS Cache consistency is being performed on this
* file.
* FS_CONSIST_ERROR There was an error during the cache
* consistency.
* FS_CONSIST_TIMEOUT There is a timeout setup for the consistency
* actions.
*/
#define FS_CONSIST_IN_PROGRESS 0x1
#define FS_CONSIST_ERROR 0x2
#define FS_CONSIST_TIMEOUT 0x4
/*
* Clients have an (arbitrary) number of minutes to complete call-back
* actions before the server blows them off and lets an open operation
* complete. This time has to be enough to let a client with a large
* main-memory cache writeback a large file.
*/
int fsconsist_TimeoutMinutes = 1;
/*
* Rpc to send when forcing a client to invalidate or write back a file.
*/
typedef struct ConsistMsg {
Fs_FileID fileID; /* Which file to invalidate. */
int flags; /* One of the flags defined below. */
int openTimeStamp; /* Open that this rpc pertains to. */
int version; /* Version number of the file */
} ConsistMsg;
/*
* Message sent when the client has completed the work requested by the server.
* This is actually the request part of the rpc transaction.
*/
typedef struct ConsistReply {
Fs_FileID fileID;
Fscache_Attributes cachedAttr;
ReturnStatus status;
} ConsistReply;
/*
* Structure used to keep track of outstanding cache consistency requests.
*/
typedef struct {
List_Links links;
int clientID;
int flags;
} ConsistMsgInfo;
/*
* Global time stamp. A time stamp is returned to clients on each open
* and on cache consistency messages. This way clients can detect races
* between open replies and consistency actions.
*/
static int openTimeStamp = 0;
/*
* Forward declarations.
*/
extern void StartConsistency _ARGS_((Fsconsist_Info *consistPtr,
int clientID, int useFlags, Boolean *cacheablePtr));
extern void UpdateList _ARGS_((Fsconsist_Info *consistPtr, int clientID,
int useFlags, Boolean cacheable, int *openTimeStampPtr));
extern ReturnStatus EndConsistency _ARGS_((Fsconsist_Info *consistPtr));
extern void ConsistTimeoutIntr _ARGS_((Timer_Ticks time, ClientData data));
extern void ConsistTimeout _ARGS_((ClientData data,
Proc_CallInfo *callInfoPtr));
extern void ClientCommand _ARGS_((Fsconsist_Info *consistPtr,
Fsconsist_ClientInfo *clientPtr, int flags));
extern void ProcessConsist _ARGS_((ClientData data,Proc_CallInfo *callInfoPtr));
extern void ProcessConsistReply _ARGS_((Fsconsist_Info *consistPtr,
int clientID, ConsistReply *replyPtr));
extern char *ConsistType _ARGS_((int flags));
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_Init --
*
* Initialize the client use information for a file. This is done
* before adding any clients so it just resets all the fields.
*
* Results:
* None.
*
* Side effects:
* Reset the client use state..
*
* ----------------------------------------------------------------------------
*
*/
void
Fsconsist_Init(consistPtr, hdrPtr)
register Fsconsist_Info *consistPtr; /* State to initialize */
Fs_HandleHeader *hdrPtr; /* Back pointer to handle */
{
Sync_LockInitDynamic(&consistPtr->lock, "Fs:consistLock");
consistPtr->flags = 0;
consistPtr->lastWriter = -1;
consistPtr->openTimeStamp = 0;
consistPtr->hdrPtr = hdrPtr;
List_Init(&consistPtr->clientList);
List_Init(&consistPtr->msgList);
consistPtr->consistDone.waiting = 0;
consistPtr->repliesIn.waiting = 0;
}
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_SyncLockCleanup --
*
* Clean up Sync_Lock tracing for the cache lock.
*
* Results:
* None.
*
* Side effects:
* As above.
*
* ----------------------------------------------------------------------------
*
*/
/*ARGSUSED*/
void
Fsconsist_SyncLockCleanup(consistPtr)
Fsconsist_Info *consistPtr; /* State to initialize */
{
Sync_LockClear(&consistPtr->lock);
}
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_MappedConsistency --
*
* Take action to ensure that everything is consistent for a
* file that is being mapped.
*
* Results:
* SUCCESS or FS_FILE_BUSY.
*
* Side effects:
* Issues cache consistency messages.
*
* ----------------------------------------------------------------------------
*
*/
/*ARGSUSED*/
ENTRY ReturnStatus
Fsconsist_MappedConsistency(handlePtr, clientID, isMapped)
Fsio_FileIOHandle *handlePtr; /* File to check consistency of. */
int clientID; /* ID of the host doing the map. */
int isMapped; /* 1 if file is being mapped. */
{
#ifdef notdef
int cacheable; /* Dummy. */
register Fsconsist_ClientInfo *clientPtr;
register Fsconsist_Info *consistPtr = &handlePtr->consist;
ReturnStatus status;
printf("Fsconsist_MappedConsistency: updating consistency (a)\n");
LOCK_MONITOR;
printf("Fsconsist_MappedConsistency: updating consistency (b)\n");
StartConsistency(consistPtr, clientID, (int)(isMapped ? FS_MAP : 0),
&cacheable);
printf("Fsconsist_MappedConsistency: updating consistency (c)\n");
LIST_FORALL(&consistPtr->clientList, (List_Links *)clientPtr) {
if (clientPtr->clientID == clientID) {
clientPtr->mapped = isMapped ? TRUE : FALSE;
}
}
printf("Fsconsist_MappedConsistency: updating consistency (d)\n");
status = EndConsistency(consistPtr);
printf("Fsconsist_MappedConsistency: updating consistency (e)\n");
UNLOCK_MONITOR;
return(status);
#endif
return SUCCESS;
}
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_FileConsistency --
*
* Take action to ensure that the caches are consistent for this
* file. This checks against use conflicts and will return an
* non-SUCCESS status if the open should fail. Otherwise this
* makes call-backs to other clients to keep caches consistent.
*
* Results:
* SUCCESS or FS_FILE_BUSY. Also, *cacheablePtr set to TRUE if the
* file is cacheable on the client. *openTimeStampPtr is set to the
* next open time stamp on the file. Clients use this timeStamp
* to catch races between open replies, which have the next timeStamp,
* and consistency messages from other opens happening
* at about the same time, which have the current timeStamp.
*
* Side effects:
* Issues cache consistency messages and adds the client to the
* list of clients of the file.
* The handle is unlocked before Fsconsist_FileConsistency is called.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY ReturnStatus
Fsconsist_FileConsistency(handlePtr, clientID, useFlags, cacheablePtr,
openTimeStampPtr)
Fsio_FileIOHandle *handlePtr; /* File to check consistency of. */
int clientID; /* ID of the host doing the open */
register int useFlags; /* useFlags from the open call */
Boolean *cacheablePtr; /* TRUE if file is cacheable. */
int *openTimeStampPtr;/* Timestamp of open. Used by clients
* to catch races between open replies
* and cache consistency messages */
{
register Fsconsist_Info *consistPtr = &handlePtr->consist;
ReturnStatus status;
LOCK_MONITOR;
/*
* Go through the list of other clients using the file checking
* for conflicts and issuing cache consistency messages.
*/
StartConsistency(consistPtr, clientID, useFlags, cacheablePtr);
/*
* Add ourselves to the list of clients using the file.
*/
UpdateList(consistPtr, clientID, useFlags, *cacheablePtr,
openTimeStampPtr);
/*
* Now that we are all set up, and have told all the other clients
* using the file what they have to do, we wait for them to finish.
*/
status = EndConsistency(consistPtr);
UNLOCK_MONITOR;
return(status);
}
/*
* ----------------------------------------------------------------------------
*
* StartConsistency --
*
* Initiate cache consistency action on a file. This decides if the
* client can cache the file, and then makes call-backs to other
* clients so that caches stay consistent. EndConsistency
* should be called later to wait for the client replies.
*
* Results:
* *cacheablePtr set to TRUE if the file is cacheable. *fileBusyPtr
* is set to FS_FILE_BUSY if the file is either being opened for execution
* and it is already open for writing or vice versa.
*
* Side effects:
* Sets the FS_CONSIST_IN_PROGRESS flag and makes call-backs to
* clients. The flag is cleared if the call-backs can't be made.
*
* ----------------------------------------------------------------------------
*
*/
INTERNAL void
StartConsistency(consistPtr, clientID, useFlags, cacheablePtr)
Fsconsist_Info *consistPtr; /* File's consistency state. */
int clientID; /* ID of host opening the file */
int useFlags; /* Indicates how they are using it */
Boolean *cacheablePtr; /* Return, TRUE if client can cache */
{
register Fsconsist_ClientInfo *clientPtr;
register Fsconsist_ClientInfo *nextClientPtr;
register Fs_ConsistStats *statPtr = &fs_Stats.consist;
register Fs_MigStats *migStatPtr = &fs_Stats.mig;
register int openForWriting = useFlags & FS_WRITE;
register Boolean cacheable;
Boolean countMigration; /* Set if we're supposed to increment a
counter for the type of consistency
performed, and cleared once the increment
is done. */
int clients = 0;
int notCaching = 0;
int writebackFlags;
/*
* Make sure that noone else is in the middle of performing cache
* consistency on this file.
*/
while (consistPtr->flags & FS_CONSIST_IN_PROGRESS) {
(void) Sync_Wait(&consistPtr->consistDone, FALSE);
}
consistPtr->flags = FS_CONSIST_IN_PROGRESS;
/*
* Determine cacheability of the file. Note the system-wide switch
* to disable client caching. There are other special cases that
* are not cached:
* 1. Swap files are not cached on clients.
* 2. Non-files, (dirs, links) are not cacheable so we don't have to
* worry about keeping them consistent. (This could be done.)
* 3. Files that are being concurrently write-shared are not cached.
* This includes one writer and one or more readers on other hosts,
* and writers on multiple hosts.
*/
cacheable = fsconsist_ClientCachingEnabled;
if (useFlags & FS_MIGRATING) {
countMigration = 1;
writebackFlags = FSCONSIST_MIGRATION;
} else {
countMigration = 0;
writebackFlags = 0;
}
if ((useFlags & FS_SWAP) && (clientID != rpc_SpriteID)) {
cacheable = FALSE;
statPtr->swap++;
} else if (((Fsio_FileIOHandle *)consistPtr->hdrPtr)->descPtr->fileType
!= FS_FILE) {
cacheable = FALSE;
statPtr->nonFiles++;
} else if (useFlags & FS_MAP) {
cacheable = FALSE;
}
if (cacheable) {
LIST_FORALL(&consistPtr->clientList, (List_Links *)clientPtr) {
if (clientPtr->mapped) {
cacheable = FALSE;
goto done;
}
if (clientPtr->clientID != clientID) {
if ((clientPtr->use.write > 0) ||
((clientPtr->use.ref > 0) && openForWriting)) {
cacheable = FALSE;
goto done;
}
}
}
} else {
countMigration = FALSE;
}
done:
/*
* Now that we know the cacheable state of the file, check the use
* by other clients, perhaps sending them cache consistency
* messages. For each message we send out (the client replies
* right-away without actually doing anything yet) ClientCommand
* adds an entry to the consistInfo's message list. Note also that
* the current client list entry can get removed as side effects of
* a call-back so we can't use a simple LIST_FOR_ALL here.
*
* Record statistics for both regular opens and migrations. Migrations
* are really a subset of regular opens since only some cases can occur.
*/
statPtr->files++;
if (countMigration) {
migStatPtr->consistActions++;
}
nextClientPtr = (Fsconsist_ClientInfo *)List_First(&consistPtr->clientList);
while (!List_IsAtEnd(&consistPtr->clientList, (List_Links *)nextClientPtr)){
clientPtr = nextClientPtr;
clientPtr->locked = FALSE;
nextClientPtr = (Fsconsist_ClientInfo *)List_Next((List_Links *)clientPtr);
/*
* Hang onto the next client element across calls to ClientCommand,
* which releases the consistency lock and may allow client list
* deletions due to garbage collection.
*/
if (!List_IsAtEnd(&consistPtr->clientList,(List_Links *)nextClientPtr)){
nextClientPtr->locked = TRUE;
}
if (clientPtr->clientID == clientID) {
/*
* Don't call back to the client doing the open. That can
* cause deadlock. Instead, that client takes care of its
* own cache via the Fscache_UpdateFile procedure.
*/
if (clientPtr->clientID == consistPtr->lastWriter) {
statPtr->writeCaching++;
} else if (clientPtr->use.ref > 0 && clientPtr->cached) {
statPtr->readCachingMyself++;
}
continue;
}
clients++;
if (!clientPtr->cached) {
/*
* Case 1, the other client isn't caching the file. Do nothing.
*/
notCaching++;
} else if (cacheable) {
if (consistPtr->lastWriter != clientPtr->clientID) {
/*
* Case 2, the other client is caching and it's ok.
*/
statPtr->readCachingOther++;
if (countMigration) {
/*
* Already caching for reading -- this migration can't
* change that.
*/
migStatPtr->readOnlyFiles++;
countMigration = 0;
}
} else if (consistPtr->lastWriter == clientID) {
/*
* Case 3, the last writer is now opening for reading.
* Its dirty cache is ok. (This case is trapped out above.)
*/
statPtr->writeCaching++;
} else {
int mode;
/*
* Case 4, the last writer needs to give us back the
* dirty blocks so the opening client will get good data.
* In the case of migration, it is possible for a writable
* file to be migrated to another host while still being
* cacheable. In that case the old client doesn't have
* any more references to the file and can't use its cached
* blocks for it anyway. Since the version number doesn't
* get incremented due to migration we have to invalidate
* at migration time.
*/
mode = FSCONSIST_WRITE_BACK_BLOCKS | writebackFlags;
if (openForWriting) {
mode |= FSCONSIST_INVALIDATE_BLOCKS;
}
ClientCommand(consistPtr, clientPtr, mode);
statPtr->writeBack++;
if (countMigration) {
migStatPtr->cacheWritableFiles++;
countMigration = 0;
}
}
} else {
if ((clientPtr->use.write == 0) && (clientPtr->use.ref > 0)) {
/*
* Case 5, another reader needs to stop caching.
*/
ClientCommand(consistPtr, clientPtr,
FSCONSIST_INVALIDATE_BLOCKS);
statPtr->readInvalidate++;
} else if (clientPtr->use.write > 0) {
/*
* Case 6, the writer needs to stop caching and give
* us back its dirty blocks.
*/
ClientCommand(consistPtr, clientPtr,
FSCONSIST_WRITE_BACK_BLOCKS |
FSCONSIST_INVALIDATE_BLOCKS | writebackFlags);
statPtr->writeInvalidate++;
}
if (countMigration) {
migStatPtr->cacheToUncacheFiles++;
countMigration = 0;
}
}
}
if (cacheable) {
statPtr->cacheable++;
if (countMigration) {
if (notCaching == clients) {
migStatPtr->uncacheToCacheFiles++;
} else {
migStatPtr->readOnlyFiles++;
}
}
} else {
statPtr->uncacheable++;
if (countMigration) {
migStatPtr->uncacheableFiles++;
}
}
statPtr->clients += clients;
statPtr->notCaching += notCaching;
*cacheablePtr = cacheable;
}
/*
* ----------------------------------------------------------------------------
*
* UpdateList --
*
* Update the state of the client that is using one of our files.
* A timestamp is generated for return to the client so it can
* catch races between the open return message and cache consistency
* messages associated with this file.
*
* Results:
* The timestamp.
*
* Side effects:
* The version number is incremented when open for writing. The
* lastWriter is remembered when opening for writing. Use counts
* are kept to reflect the clients use of the file. Finally,
* client list entries for hosts no longer using or caching the
* file are deleted.
*
* ----------------------------------------------------------------------------
*/
INTERNAL void
UpdateList(consistPtr, clientID, useFlags, cacheable, openTimeStampPtr)
register Fsconsist_Info *consistPtr;/* Consistency state for the file. */
int clientID; /* ID of client using the file */
int useFlags; /* FS_READ|FS_WRITE|FS_EXECUTE */
Boolean cacheable; /* TRUE if client is caching the file */
int *openTimeStampPtr;/* Generated for the client so it can
* catch races between the return from
* this open and other cache messages */
{
register Fsconsist_ClientInfo *clientPtr; /* State for other clients */
/*
* Add the client to the I/O handle client list.
*/
clientPtr = Fsconsist_IOClientOpen(&consistPtr->clientList, clientID,
useFlags, cacheable);
if (cacheable && (useFlags & FS_WRITE)) {
consistPtr->lastWriter = clientID;
}
/*
* Return a time stamp for the open. This timestamp is used by clients
* to catch races between the reply message for an open, and a cache
* consistency message generated from a different open happening at
* about the same time.
*/
if (openTimeStampPtr != (int *)NIL) {
openTimeStamp++;
*openTimeStampPtr =
consistPtr->openTimeStamp =
clientPtr->openTimeStamp = openTimeStamp;
} else {
consistPtr->openTimeStamp = clientPtr->openTimeStamp = 0;
}
}
/*
* ----------------------------------------------------------------------------
*
* EndConsistency --
* Wait for cache consistency actions to complete.
*
* Results:
* SUCCESS or FS_NO_DISK_SPACE. We have to abort an open if a client
* cannot write back its cache in response to a consistency message.
* This only happens if the disk is full. If the client is down we
* won't wait for it. If it is not really down we treat it as
* down and will abort its attempt to re-open later.
*
* Side effects:
* Notifies the consistDone condition to allow someone else to
* open the file. Clears the FS_CONSIST_IN_PROGRES flag.
*
* ----------------------------------------------------------------------------
*
*/
INTERNAL ReturnStatus
EndConsistency(consistPtr)
Fsconsist_Info *consistPtr;
{
register ReturnStatus status;
Timer_QueueElement timeout;
/*
* Set up a timeout in case a flakey client can't complete
* its consistency actions. We pick an arbitrary "long" interval
* and just abort after that. A more complex solution would be
* to re-iterate what ever high level operation we are doing.
*/
timeout.routine = ConsistTimeoutIntr;
timeout.interval = timer_IntOneMinute * fsconsist_TimeoutMinutes;
timeout.clientData = (ClientData)consistPtr;
consistPtr->flags |= FS_CONSIST_TIMEOUT;
Timer_ScheduleRoutine(&timeout, TRUE);
while (!List_IsEmpty(&consistPtr->msgList)) {
(void) Sync_Wait(&consistPtr->repliesIn, FALSE);
}
if (consistPtr->flags & FS_CONSIST_TIMEOUT) {
(void)Timer_DescheduleRoutine(&timeout);
consistPtr->flags &= ~FS_CONSIST_TIMEOUT;
}
if (consistPtr->flags & FS_CONSIST_ERROR) {
/*
* The only reason consistency actions fail altogether is when
* a client can't do a writeback because there isn't enough
* disk space here. Crashed clients don't matter.
*/
printf("EndConsistency: consistency failed for %s: no disk space\n",
Fsutil_HandleName(consistPtr->hdrPtr)); /* DEBUG */
status = FS_NO_DISK_SPACE;
} else {
status = SUCCESS;
}
consistPtr->flags = 0;
Sync_Broadcast(&consistPtr->consistDone);
return(status);
}
/*
*----------------------------------------------------------------------
*
* ConsistTimeoutIntr --
*
* Routine called at timer-interrupt time if a client has not
* completed consistency actions. This turns around and does
* a Proc_CallFunc to invoke ConsistTimeout at process level.
* ConsistTimeout prints a warning and removes the information
* about the now aborted consistency request so that the higher-level
* operation can complete.
*
* Results:
* None.
*
* Side effects:
* Triggers a call to ConsistTimeout, which in turn erases the
* consistPtr->msgList and notifies the consistPtr->repliesIn condition.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
void
ConsistTimeoutIntr(time, data)
Timer_Ticks time; /* The time we timed out at. */
ClientData data; /* A pointer to a Fsconsist_Info */
{
Fsconsist_Info *consistPtr = (Fsconsist_Info *)data;
consistPtr->flags &= ~FS_CONSIST_TIMEOUT;
Proc_CallFunc(ConsistTimeout, (ClientData)consistPtr, 0);
}
void
ConsistTimeout(data, callInfoPtr)
ClientData data; /* A pointer to a Fsconsist_Info */
Proc_CallInfo *callInfoPtr;
{
Fsconsist_Info *consistPtr = (Fsconsist_Info *)data;
ConsistMsgInfo *msgPtr;
int ref, write, exec;
LOCK_MONITOR;
while (!List_IsEmpty(&consistPtr->msgList)) {
msgPtr = (ConsistMsgInfo *)List_First(&consistPtr->msgList);
printf("ConsistTimeout (%d minutes) client %d %s file <%d,%d> \"%s\"\n",
fsconsist_TimeoutMinutes,
msgPtr->clientID, ConsistType(msgPtr->flags),
consistPtr->hdrPtr->fileID.major,
consistPtr->hdrPtr->fileID.minor,
Fsutil_HandleName(consistPtr->hdrPtr));
Fsconsist_IOClientKill(&consistPtr->clientList, msgPtr->clientID,
&ref, &write, &exec);
printf("\tClient state killed: %d refs %d write %d exec\n",
ref, write, exec);
if (msgPtr->clientID == consistPtr->lastWriter) {
consistPtr->lastWriter = -1;
}
List_Remove((List_Links *)msgPtr);
free((Address)msgPtr);
}
Sync_Broadcast(&consistPtr->repliesIn);
callInfoPtr->interval = 0; /* No more invocations, please */
UNLOCK_MONITOR;
}
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_ReopenClient --
*
* Conflict checking has already been done for the re-open, and this
* routine updates global use counts and the client list so that
* regular opens know the file is being used. This is called with
* the handle locked and it also grabs the consistency monitor lock
* to update the client list.
* Consistency call-backs are made later by Fsconsist_ReopenConsistency.
*
* Results:
* None.
*
* Side effects:
* Updates the global use counts of the file, the last writer of
* the file, and the client list entry for this client. Note that
* by updating the lastWriter here we may cause a call-back to
* the re-opening client during Fsconsist_ReopenConsistency. This is the
* best we can do if another client has slipped in and opened
* for reading already.
*
* ----------------------------------------------------------------------------
*/
ENTRY void
Fsconsist_ReopenClient(handlePtr, clientID, use, haveDirtyBlocks)
Fsio_FileIOHandle *handlePtr; /* Should be LOCKED. */
int clientID; /* The client who is opening the file.*/
Fsio_UseCounts use; /* Clients usage of the file */
Boolean haveDirtyBlocks;/* TRUE if client expects it to be
* cacheable because it has
* outstanding dirty blocks. */
{
register Fsconsist_Info *consistPtr = &handlePtr->consist;
register Fsconsist_ClientInfo *clientPtr;
Boolean found;
LOCK_MONITOR;
found = FALSE;
LIST_FORALL(&(consistPtr->clientList), (List_Links *)clientPtr) {
if (clientPtr->clientID == clientID) {
found = TRUE;
handlePtr->use.ref += use.ref - clientPtr->use.ref;
handlePtr->use.write += use.write - clientPtr->use.write;
handlePtr->use.exec += use.exec - clientPtr->use.exec;
clientPtr->use = use;
clientPtr->cached = haveDirtyBlocks;
if ((handlePtr->use.ref < 0) || (handlePtr->use.write < 0) ||
(handlePtr->use.exec < 0)) {
panic("Fsconsist_ReopenClient: client %d ref %d write %d exec %d\n" ,
clientID,
handlePtr->use.ref, handlePtr->use.write,
handlePtr->use.exec);
}
} else if ((clientPtr->use.ref > 0) && haveDirtyBlocks) {
/*
* Oops, another client is reading this file but the re-opening
* client has dirty blocks for it. We've already checked the
* version number so we know another client isn't writing.
* We allow this conflict to happen - the regular cache
* consistency routines will tell the reading client to
* stop caching, and the writing client will write-back
* its blocks as soon as possible. This leaves a window
* for inconsistent reads, but the outstanding dirty blocks
* are not lost.
*/
printf("FsReopenHandle: file \"%s\" <%d,%d>: client %d has dirty blocks, but client %d is using\n",
Fsutil_HandleName(handlePtr),
handlePtr->hdr.fileID.major, handlePtr->hdr.fileID.minor,
clientID, clientPtr->clientID);
}
}
if (!found) {
INSERT_CLIENT(&consistPtr->clientList, clientPtr, clientID);
clientPtr->use = use;
clientPtr->cached = haveDirtyBlocks;
handlePtr->use.ref += use.ref;
handlePtr->use.write += use.write;
handlePtr->use.exec += use.exec;
}
if (haveDirtyBlocks) {
if (consistPtr->lastWriter != -1 &&
consistPtr->lastWriter != clientID) {
/*
* Version number checking should have prevented this.
*/
printf("FsReopenHandle: file \"%s\" <%d,%d>: Client %d with dirty blocks not last writer %d\n",
Fsutil_HandleName(handlePtr),
handlePtr->hdr.fileID.major, handlePtr->hdr.fileID.minor,
clientID, consistPtr->lastWriter);
} else {
consistPtr->lastWriter = clientID;
}
} else if (consistPtr->lastWriter == clientID) {
consistPtr->lastWriter = -1;
}
UNLOCK_MONITOR;
}
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_ReopenConsistency --
*
* Perform cache consistency actions for a file that the client had
* open before we crashed. This is similar to regular cache consistency,
* except that the client list has already been updated by
* Fsconsist_ReopenClient. This can result in a call-back to the
* re-opening client asking to write back its version of the file.
*
* Results:
* TRUE if could provide the cacheability expected, FALSE otherwise.
*
* Side effects:
* *cacheablePtr is TRUE if the file is to be cached by the client.
* *openTimeStampPtr is set for use by clients. They keep this timestamp
* in order to catch races between the return from their open request
* (which we are part of) and cache consistency messages resulting from
* opens occuring at about the same time.
*
* ----------------------------------------------------------------------------
*/
ENTRY ReturnStatus
Fsconsist_ReopenConsistency(handlePtr, clientID, use, swap, cacheablePtr,
openTimeStampPtr)
Fsio_FileIOHandle *handlePtr; /* Should be UNLOCKED. */
int clientID; /* The client who is opening the file.*/
Fsio_UseCounts use; /* Clients usage of the file */
int swap; /* 0 or FS_SWAP to indicate swap file.*/
Boolean *cacheablePtr; /* IN: TRUE if client expects it to be
* cacheable, i.e. has dirty blocks.
* OUT: Cacheability of the file. */
int *openTimeStampPtr; /* Place to return a timestamp for
* this open.*/
{
int useFlags;
Boolean cacheable;
ReturnStatus status;
register Fsconsist_Info *consistPtr = &handlePtr->consist;
register Fsconsist_ClientInfo *clientPtr;
LOCK_MONITOR;
useFlags = swap;
if (use.ref > use.write + use.exec) {
useFlags |= FS_READ;
}
if (use.write > 0 || *cacheablePtr) {
useFlags |= FS_WRITE;
}
if (use.exec > 0) {
useFlags |= FS_EXECUTE;
}
if (useFlags == 0) {
/*
* The client isn't using the file anymore so clean up state.
*/
LIST_FORALL(&(consistPtr->clientList), (List_Links *)clientPtr) {
if (clientPtr->clientID == clientID) {
REMOVE_CLIENT(clientPtr);
break;
}
}
*openTimeStampPtr = consistPtr->openTimeStamp;
*cacheablePtr = FALSE;
status = SUCCESS;
} else {
StartConsistency(consistPtr, clientID, useFlags, &cacheable);
/*
* Get a new openTimeStamp so the client can detect races between
* the reopen return and consistency messages generated by other
* opens happening right now (or real soon).
* FIX ME. The file version number should be used instead.
*/
openTimeStamp++;
*openTimeStampPtr = consistPtr->openTimeStamp = openTimeStamp;
/*
* Mark the client as caching or not.
*/
*cacheablePtr = cacheable;
if ((useFlags & FS_WRITE) && cacheable) {
consistPtr->lastWriter = clientID;
}
/*
* We are careful to search for the client list entry again
* because it might go away during concurrent operations.
*/
LIST_FORALL(&consistPtr->clientList, (List_Links *)clientPtr) {
if (clientPtr->clientID == clientID) {
clientPtr->openTimeStamp = openTimeStamp;
clientPtr->cached = cacheable;
break;
}
}
/*
* Wait for cache consistency call-backs to complete.
*/
status = EndConsistency(consistPtr);
}
UNLOCK_MONITOR;
return(status);
}
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_MigrateConsistency --
*
* Shift the references on a file from one client to another. If
* the stream to this handle is not shared across network this
* is done by first removing the useCounts due to the srcClient,
* and them performing the regular cache consistency algorithm as
* if the dstClient is opening the file. If the stream is shared
* things are more complicated. We must not close the original
* client until all stream references to its I/O handle have migrated,
* and we must be careful to not add too many references to the
* new client.
*
* Results:
* SUCCESS, unless there is a cache consistency conflict detected.
*
* Side effects:
* The HANDLE RETURNS UNLOCKED. Also, full-fledged cache consistency
* actions are taken.
*
* ----------------------------------------------------------------------------
*/
ENTRY ReturnStatus
Fsconsist_MigrateConsistency(handlePtr, srcClientID, dstClientID, useFlags,
closeSrc, cacheablePtr, openTimeStampPtr)
Fsio_FileIOHandle *handlePtr; /* Needs to be UNLOCKED */
int srcClientID; /* ID of client using the file */
int dstClientID; /* ID of client using the file */
int useFlags; /* FS_READ|FS_WRITE|FS_EXECUTE
* FS_RMT_SHARED if shared now.
* FS_NEW_STREAM if dstClientID is
* getting stream for first time.
* FS_MIGRATED_FILE is set to tell
* the low-level consistency routines
* to record statistics. */
Boolean closeSrc; /* TRUE if should close source client.
* Set by Fsio_StreamMigClient */
Boolean *cacheablePtr; /* Return - Cachability of file */
int *openTimeStampPtr;/* Generated for the client so it can
* catch races between the return from
* this open and other cache messages */
{
register Fsconsist_Info *consistPtr = &handlePtr->consist;
Boolean cache;
register ReturnStatus status;
LOCK_MONITOR;
cache = (srcClientID == consistPtr->lastWriter);
if (closeSrc) {
/*
* Remove references due to the original client so it doesn't confuse
* the regular cache consistency algorithm. Note that this call
* doesn't disturb the last writer state so any dirty blocks on
* the old client will get handled properly.
*/
if (!Fsconsist_IOClientClose(&consistPtr->clientList, srcClientID,
useFlags, &cache)) {
printf("Fsconsist_MigrateConsistency, srcClient %d unknown for %s %s <%d,%d>\n",
srcClientID,
Fsutil_FileTypeToString(handlePtr->hdr.fileID.type),
Fsutil_HandleName(handlePtr),
handlePtr->hdr.fileID.major, handlePtr->hdr.fileID.minor);
}
}
/*
* The rest of this is like regular cache consistency.
*/
StartConsistency(consistPtr, dstClientID, (int) (useFlags | FS_MIGRATING),
cacheablePtr);
if (useFlags & FS_NEW_STREAM) {
/*
* The client is getting the stream to this I/O handle for
* the first time so we should add it as a client. We are
* careful about this because there is only one reference to
* the I/O handle per client per stream.
*/
UpdateList(consistPtr, dstClientID, useFlags, *cacheablePtr,
openTimeStampPtr);
}
status = EndConsistency(consistPtr);
UNLOCK_MONITOR;
return(status);
}
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_GetClientAttrs --
*
* This does call-backs to clients to flush back cached attributes.
* Files that are being executed are treated as a special case. They
* are not being written so we, the server, have the right size and
* modify time. Instead of getting access times from clients (there
* could be lots and lots) we just return a flag that says the
* file is being executed. Our caller will presumably use the
* the current time for the access time in this case.
*
* Results:
* *isExecedPtr set to TRUE if the file is being executed.
*
* Side effects:
* RPC's may be done to clients using the file to tell them to write
* back their cached attributes.
* This unlocks the handle while waiting for clients, but
* re-locks it before returning.
*
* ----------------------------------------------------------------------------
*/
ENTRY void
Fsconsist_GetClientAttrs(handlePtr, clientID, isExecedPtr)
register Fsio_FileIOHandle *handlePtr;
int clientID; /* The client who is doing the stat.*/
Boolean *isExecedPtr; /* TRUE if file is being executed.
* Our caller will use the current
* time for the access time in this
* case */
{
register Boolean isExeced = FALSE;
register Fsconsist_Info *consistPtr = &handlePtr->consist;
register Fsconsist_ClientInfo *clientPtr;
register Fsconsist_ClientInfo *nextClientPtr;
/*
* Unlock the handle so other operations on the file can proceed while
* we probe around the network for the most up-to-date attributes.
* Otherwise the following deadlock is possible:
* Client 1 does a get attributes about the same time client 2 does
* a close. Client 2 has its handle locked during the close, but we
* will be calling back to it to get its attributes. Our callback can't
* start until client 2 unlocks its handle, but it can't unlock it's
* handle until the close finishes. The close can't finish because
* it is blocked here on the locked handle.
*/
Fsutil_HandleUnlock(handlePtr);
LOCK_MONITOR;
/*
* Make sure that noone else is in the middle of performing cache
* consistency on this handle. If so wait until they are done.
*/
while (consistPtr->flags & FS_CONSIST_IN_PROGRESS) {
(void) Sync_Wait(&consistPtr->consistDone, FALSE);
}
consistPtr->flags = FS_CONSIST_IN_PROGRESS;
/*
* Go through the set of clients using the file and see if they
* are caching attributes.
*/
nextClientPtr = (Fsconsist_ClientInfo *)List_First(&consistPtr->clientList);
while (!List_IsAtEnd(&consistPtr->clientList, (List_Links *)nextClientPtr)){
clientPtr = nextClientPtr;
clientPtr->locked = FALSE;;
nextClientPtr = (Fsconsist_ClientInfo *)List_Next((List_Links *)clientPtr);
/*
* Hang onto the next client list element across calls to ClientCommand,
* which releases the consistency lock and allows list deletions.
*/
if (!List_IsAtEnd(&consistPtr->clientList,(List_Links *)nextClientPtr)){
nextClientPtr->locked = TRUE;
}
if (clientPtr->use.exec > 0) {
isExeced = TRUE;
}
if ((clientPtr->cached) && (clientPtr->use.ref > 0)) {
if ((clientPtr->clientID != clientID) &&
(clientPtr->use.exec == 0)) {
/*
* Don't call back to our caller because it will check
* its own attributes later. Also, don't send rpcs to
* clients executing files. For these clients we just
* set the access time to the current time. This
* is an optimization to not make stating of binaries
* abysmally slow.
*/
ClientCommand(consistPtr, clientPtr, FSCONSIST_WRITE_BACK_ATTRS);
}
}
}
/*
* Now that we are all set up, and have told all the other clients using
* the file what they have to do, we wait for them to finish.
*/
(void)EndConsistency(consistPtr);
*isExecedPtr = isExeced;
UNLOCK_MONITOR;
Fsutil_HandleLock(handlePtr);
}
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_Close --
*
* A thin layer on top of Fsconsist_IOClientClose that also cleans up
* the last writer of a file.
*
* Results:
* TRUE if there was a record that the client was using the file.
* This is used to trap out invalid closes. Also, *wasCachedPtr
* is set to TRUE if the file (in particular, its attributes) was
* cached on the client.
*
* Side effects:
* The client list entry is removed. If the client was
* the last writer but has no dirty blocks (as indicated by the flags)
* then the last writer field of the consist info is cleared.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY Boolean
Fsconsist_Close(consistPtr, clientID, flags, wasCachedPtr)
register Fsconsist_Info *consistPtr; /* Handle of file being closed */
int clientID; /* Host ID of client that had it open */
register int flags; /* Flags from the stream. */
Boolean *wasCachedPtr; /* TRUE upon return if the client was
* caching (attributes) of the file. */
{
LOCK_MONITOR;
*wasCachedPtr = (consistPtr->lastWriter == clientID);
if (!Fsconsist_IOClientClose(&consistPtr->clientList, clientID, flags,
wasCachedPtr)) {
UNLOCK_MONITOR;
return(FALSE);
}
if ((consistPtr->lastWriter != -1) && (flags & FS_LAST_DIRTY_BLOCK)) {
if (clientID != consistPtr->lastWriter) {
/*
* Probably a client error with the lastWriter. We print
* a warning and then nuke the lastWriter field below.
*/
printf("%s, \"%s\" <%d,%d>: client %d not last writer %d, %s\n",
"Fsconsist_Close",
Fsutil_HandleName(consistPtr->hdrPtr),
consistPtr->hdrPtr->fileID.major,
consistPtr->hdrPtr->fileID.minor,
clientID, consistPtr->lastWriter,
(*wasCachedPtr) ? "was cached" : "wasn't cached");
}
consistPtr->lastWriter = -1;
}
UNLOCK_MONITOR;
return(TRUE);
}
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_NumClients --
*
* Returns the number of clients in the client list for a file.
* Called to see if it's ok to scavenge the file handle.
*
* Results:
* The number of clients in the client list.
*
* Side effects:
* Unused client list entries are cleaned up. We only need to remember
* clients that are actively using the file or who have dirty blocks
* because they are the last writer.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY int
Fsconsist_NumClients(consistPtr)
register Fsconsist_Info *consistPtr; /* Handle of file being closed */
{
register int numClients = 0;
register Fsconsist_ClientInfo *clientPtr;
register Fsconsist_ClientInfo *nextClientPtr;
LOCK_MONITOR;
if (consistPtr->flags & FS_CONSIST_IN_PROGRESS) {
/*
* Not safe to mess with list during consistency.
*/
numClients = 1;
goto exit;
}
nextClientPtr = (Fsconsist_ClientInfo *)List_First(&consistPtr->clientList);
while (!List_IsAtEnd(&consistPtr->clientList, (List_Links *)nextClientPtr)){
clientPtr = nextClientPtr;
nextClientPtr = (Fsconsist_ClientInfo *)List_Next((List_Links *)clientPtr);
/*
* Nuke the client list entry if the client isn't using the file now,
* and it isn't a remote client holding dirty blocks,
* and this element isn't locked by another process doing consistency.
*/
if (clientPtr->use.ref == 0 &&
((clientPtr->clientID == rpc_SpriteID) ||
(clientPtr->clientID != consistPtr->lastWriter)) &&
!clientPtr->locked) {
REMOVE_CLIENT(clientPtr);
} else {
numClients++;
}
}
exit:
UNLOCK_MONITOR;
return(numClients);
}
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_DeleteLastWriter --
*
* Remove the last writer from the consistency list. This is called
* from the write rpc stub when the last block of a file comes
* in from a remote client.
*
* Results:
* None.
*
* Side effects:
* Removes the client list entry for the last writer if the last
* writer is no longer using the file.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY void
Fsconsist_DeleteLastWriter(consistPtr, clientID)
Fsconsist_Info *consistPtr;
int clientID;
{
register Fsconsist_ClientInfo *clientPtr;
LOCK_MONITOR;
if (consistPtr->flags & FS_CONSIST_IN_PROGRESS) {
/*
* Not safe to mess with the list during consistency. We are called
* from Fsrmt_RpcWrite on the client's last block, but we will
* delete the last writer in ProcessConsistReply if the write-back
* is forced as part of cache consistency.
*/
UNLOCK_MONITOR;
return;
}
LIST_FORALL(&consistPtr->clientList, (List_Links *) clientPtr) {
if (clientPtr->clientID == clientID) {
if (clientPtr->use.ref == 0 &&
consistPtr->lastWriter == clientID) {
if (!clientPtr->locked) {
Fsrecov_HandleState recovInfo;
ReturnStatus status;
REMOVE_CLIENT(clientPtr);
/*
* If the handle had a 0 ref count but was still in the
* recovery box due to having dirty blocks in the client's
* cache, we can now get rid of it.
*/
if (recov_Transparent &&
Fsrecov_GetHandle(consistPtr->hdrPtr->fileID,
clientID, &recovInfo, FALSE) == SUCCESS) {
if (recovInfo.use.ref <= 0) {
status = Fsrecov_DeleteHandle(consistPtr->hdrPtr,
clientID, FS_WRITE | FS_LAST_DIRTY_BLOCK);
if (status != SUCCESS) {
/* Should I panic? */
printf("Fsconsist_ClientRemoveCallback: couldn't ");
printf("delete handle from recov box.\n");
}
} else {
printf("Fsconsist_ClientRemoveCallback: ref count ");
panic("on object is too high.");
}
}
}
consistPtr->lastWriter = -1;
break;
}
}
}
UNLOCK_MONITOR;
}
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_ClientRemoveCallback --
*
* Called when a file is deleted. Send an rpc to the client who has
* dirty blocks cached telling it to delete them from its cache.
* We may have forgotten about clients with clean blocks so we can't
* call them back. Instead we depend on the version number being
* incremented to catch old blocks left in caches. There are two
* reasons to tell the last writer - first, it prevents needless
* write-backs, which consumes our CPU, and it is nice to the client
* because it can make room in its cache by deleting the file.
*
* Results:
* None.
*
* Side effects:
* Any remotely cached dirty data is invalidated.
* All the entries in the client list are deleted.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY void
Fsconsist_ClientRemoveCallback(consistPtr, clientID)
Fsconsist_Info *consistPtr; /* File to check */
int clientID; /* Client who is removing the file. This
* host is not contacted via call-back.
* Instead, the current RPC (close) should
* return FS_FILE_REMOVED. Note, a callback
* is made during a remove-by-name. */
{
register Fsconsist_ClientInfo *clientPtr;
int curClientID;
Fsutil_HandleUnlock(consistPtr->hdrPtr);
LOCK_MONITOR;
while (consistPtr->flags & FS_CONSIST_IN_PROGRESS) {
(void) Sync_Wait(&consistPtr->consistDone, FALSE);
}
consistPtr->flags = FS_CONSIST_IN_PROGRESS;
/*
* Loop through the list notifying clients and deleting client elements.
*/
while (!List_IsEmpty((List_Links *)&consistPtr->clientList)) {
clientPtr = (Fsconsist_ClientInfo *)
List_First((List_Links *) &consistPtr->clientList);
curClientID = clientPtr->clientID;
if (clientPtr->use.ref > 0) {
printf("Fsconsist_ClientRemoveCallback: Client %d using removed file <%s>\n",
curClientID, Fsutil_HandleName(consistPtr->hdrPtr));
} else if (consistPtr->lastWriter != -1) {
if (clientPtr->clientID != consistPtr->lastWriter) {
printf("Fsconsist_ClientRemoveCallback: \"%s\" <%d,%d> client %d not last writer (%d).\n",
Fsutil_HandleName(consistPtr->hdrPtr),
consistPtr->hdrPtr->fileID.major,
consistPtr->hdrPtr->fileID.minor,
clientPtr->clientID, consistPtr->lastWriter);
} else if (clientPtr->clientID != clientID) {
Fsrecov_HandleState recovInfo;
ReturnStatus status;
/*
* Tell the client caching the file to remove it from its cache.
* This should only be the last writer as we are called only
* when it is truely time to remove the file.
*/
ClientCommand(consistPtr, clientPtr, FSCONSIST_DELETE_FILE);
(void)EndConsistency(consistPtr);
/*
* If the handle had a 0 ref count but was still in the
* recovery box due to having dirty blocks in the client's
* cache, we can now get rid of it.
*/
if (recov_Transparent &&
Fsrecov_GetHandle(consistPtr->hdrPtr->fileID,
clientPtr->clientID, &recovInfo, FALSE) == SUCCESS) {
if (recovInfo.use.ref <= 0) {
status = Fsrecov_DeleteHandle(consistPtr->hdrPtr,
clientPtr->clientID,
FS_WRITE | FS_LAST_DIRTY_BLOCK);
if (status != SUCCESS) {
/* Should I panic? */
printf("Fsconsist_ClientRemoveCallback: couldn't ");
printf("delete handle from recov box.\n");
}
}
}
}
}
/*
* We have to check carefully that the client element is still
* here because it may already be removed by ClientKill.
*/
LIST_FORALL((List_Links *)&consistPtr->clientList,
(List_Links *)clientPtr) {
if (clientPtr->clientID == curClientID) {
REMOVE_CLIENT(clientPtr);
break;
}
}
}
consistPtr->flags = 0;
consistPtr->lastWriter = -1;
UNLOCK_MONITOR;
Fsutil_HandleLock(consistPtr->hdrPtr);
}
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_Kill --
*
* Find and remove the given client in the list for the handle. The
* number of client references, writers, and executers is returned
* so our caller can clean up the reference counts in the handle.
*
* Results:
* *inUsePtr set to TRUE if the client has the file open, *writingPtr
* set to TRUE if the client has the file open for writing, and
* *executingPtr set to TRUE if the client has the file open for
* execution.
*
* Side effects:
* If this client was the last writer for the file then the last
* writer field in the handle is set to -1.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY void
Fsconsist_Kill(consistPtr, clientID, refPtr, writePtr, execPtr)
Fsconsist_Info *consistPtr; /* Consistency state from which to remove
* the client. */
int clientID; /* Client to delete. */
int *refPtr; /* Number of times client has file open. */
int *writePtr; /* Number of times client is writing file. */
int *execPtr; /* Number of times clients is executing file.*/
{
register ConsistMsgInfo *msgPtr;
LOCK_MONITOR;
Fsconsist_IOClientKill(&consistPtr->clientList, clientID, refPtr, writePtr,
execPtr);
if (consistPtr->lastWriter == clientID) {
consistPtr->lastWriter = -1;
}
/*
* Remove the client from the list of clients involved in a cache
* consistency action so we don't hang on the dead client.
*/
LIST_FORALL(&(consistPtr->msgList), (List_Links *) msgPtr) {
if (msgPtr->clientID == clientID) {
List_Remove((List_Links *) msgPtr);
free((Address) msgPtr);
Sync_Broadcast(&consistPtr->repliesIn);
break;
}
}
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------------
*
* Fsconsist_GetAllDirtyBlocks --
*
* Retrieve dirty blocks from all clients that have files open on the
* given domain. This is called when a disk is being detached. We
* have to get any outstanding data before taking the disk off-line.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------------
*/
void
Fsconsist_GetAllDirtyBlocks(domain, invalidate)
int domain; /* Domain to get dirty blocks for. */
Boolean invalidate; /* Remove file from cache after getting blocks
* back. */
{
Hash_Search hashSearch;
register Fs_HandleHeader *hdrPtr;
Hash_StartSearch(&hashSearch);
for (hdrPtr = Fsutil_GetNextHandle(&hashSearch);
hdrPtr != (Fs_HandleHeader *) NIL;
hdrPtr = Fsutil_GetNextHandle(&hashSearch)) {
if (hdrPtr->fileID.type == FSIO_LCL_FILE_STREAM &&
hdrPtr->fileID.major == domain) {
register Fsio_FileIOHandle *handlePtr =
(Fsio_FileIOHandle *) hdrPtr;
Fsconsist_FetchDirtyBlocks(&handlePtr->consist, invalidate);
}
Fsutil_HandleUnlock(hdrPtr);
}
}
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_FetchDirtyBlocks --
*
* Fetch dirty blocks back from the last writer of the file.
* This is called when a domain is being detached (dis-mounted)
* and we want all dirty data to be written back first.
*
* Results:
* None.
*
* Side effects:
* If the last writer doesn't have the file actively open, then it is
* no longer the last writer and it is deleted from the client list.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY void
Fsconsist_FetchDirtyBlocks(consistPtr, invalidate)
Fsconsist_Info *consistPtr; /* Consistency state for file. */
Boolean invalidate; /* If TRUE the client is told to invalidate
* after writing back the blocks */
{
register Fsconsist_ClientInfo *clientPtr;
register Fsconsist_ClientInfo *nextClientPtr;
Fsutil_HandleUnlock(consistPtr->hdrPtr);
LOCK_MONITOR;
/*
* Make sure that no one else is in the middle of performing cache
* consistency on this handle. If so wait until they are done.
*/
while (consistPtr->flags & FS_CONSIST_IN_PROGRESS) {
(void) Sync_Wait(&consistPtr->consistDone, FALSE);
}
/*
* See if there are any dirty blocks to fetch.
*/
if (consistPtr->lastWriter == -1 ||
consistPtr->lastWriter == rpc_SpriteID) {
UNLOCK_MONITOR;
Fsutil_HandleLock(consistPtr->hdrPtr);
return;
}
consistPtr->flags = FS_CONSIST_IN_PROGRESS;
/*
* There is a last writer. In this case the only thing on the list is
* the client that is the last writer because we know the domain for
* the file is in-active.
*/
nextClientPtr = (Fsconsist_ClientInfo *)List_First(&consistPtr->clientList);
while (!List_IsAtEnd(&consistPtr->clientList, (List_Links *)nextClientPtr)){
clientPtr = nextClientPtr;
clientPtr->locked = FALSE;
nextClientPtr = (Fsconsist_ClientInfo *)List_Next((List_Links *)clientPtr);
if (!List_IsAtEnd(&consistPtr->clientList,(List_Links *)nextClientPtr)){
nextClientPtr->locked = TRUE;
}
if (clientPtr->clientID != consistPtr->lastWriter) {
Fsutil_HandleLock(consistPtr->hdrPtr);
consistPtr->flags = 0;
UNLOCK_MONITOR;
panic("Fsconsist_FetchDirtyBlocks: Non last writer in list.\n");
return;
} else if (clientPtr->use.write > 0) {
/*
* The client has it actively open for writing. In this case don't
* do anything because he can have more dirty blocks anyway.
*/
} else {
register int flags = FSCONSIST_WRITE_BACK_BLOCKS;
if (invalidate) {
flags |= FSCONSIST_INVALIDATE_BLOCKS;
}
ClientCommand(consistPtr, clientPtr, flags);
(void)EndConsistency(consistPtr);
}
}
consistPtr->flags = 0;
UNLOCK_MONITOR;
Fsutil_HandleLock(consistPtr->hdrPtr);
return;
}
/*
* The consistency messages are issued in two parts. In the first phase
* the server issues all the clients commands, but the clients return
* an RPC reply immediately and schedule ProcessConsist in the background
* to actually effect the cache consistency. The second phase consists
* of the server waiting around for a return RPC by the client that
* indicates that it is done.
*/
void ProcessConsist();
void ProcessConsistReply();
/*
* Consist message information.
*/
typedef struct ConsistItem {
int serverID;
ConsistMsg args;
} ConsistItem;
/*
* ----------------------------------------------------------------------------
*
* ClientCommand --
*
* Send an rpc to a client telling him to perform some cache
* consistency operation. Also put the client onto a list of
* outstanding cache consistency messages.
*
* Results:
* The status from the RPC.
*
* Side effects:
* All locks except the consistency-in-progress lock are released
* during the call back to the client. This allows operations like
* write-backs and unrelated closes of the file to complete.
* However, this means that the state of a file can change considerably
* during a call-back. Of concern to routines in this file is that
* client list entries might get deleted, so LIST_FORALL doesn't work.
* Callers of ClientCommand have to be coded to reflect this.
*
* ----------------------------------------------------------------------------
*
*/
INTERNAL void
ClientCommand(consistPtr, clientPtr, flags)
register Fsconsist_Info *consistPtr; /* Consistency state of file */
Fsconsist_ClientInfo *clientPtr; /* State of other client's cache */
int flags; /* Command for the other client */
{
Rpc_Storage storage;
ConsistMsg consistRpc;
ReturnStatus status;
ConsistMsgInfo *msgPtr;
int numRefusals;
if (clientPtr->clientID == rpc_SpriteID) {
/*
* Don't issue commands to ourselves (the server) because the commands
* issued to the other clients (write-back, invalidate, etc.) will
* all result in a consistent server cache anyway.
*/
if (flags & FSCONSIST_INVALIDATE_BLOCKS) {
/*
* If we told ourselves to invalidate the file then mark us
* as not caching the file.
*/
clientPtr->cached = FALSE;
}
if (flags & FSCONSIST_WRITE_BACK_BLOCKS) {
/*
* We already have the most recent blocks.
*/
consistPtr->lastWriter = -1;
}
if (clientPtr->use.ref == 0 &&
consistPtr->lastWriter != clientPtr->clientID) {
FSCACHE_DEBUG_PRINT1("ClientCommand: Removing %d ",
clientPtr->clientID);
REMOVE_CLIENT(clientPtr);
}
return;
}
/*
* Map to the client's view of the file (i.e. remote).
*/
consistRpc.fileID = consistPtr->hdrPtr->fileID;
if (consistRpc.fileID.type != FSIO_LCL_FILE_STREAM) {
panic("ClientCommand, bad stream type <%d>\n",
consistRpc.fileID.type);
} else {
consistRpc.fileID.type = FSIO_RMT_FILE_STREAM;
}
/*
* The openTimeStamp lets the client catch races between this message
* and the reply to an open it may be making at the same time.
*/
consistRpc.flags = flags;
consistRpc.openTimeStamp = clientPtr->openTimeStamp;
consistRpc.version =
((Fsio_FileIOHandle *)consistPtr->hdrPtr)->cacheInfo.version;
storage.requestParamPtr = (Address) &consistRpc;
storage.requestParamSize = sizeof(ConsistMsg);
storage.requestDataPtr = (Address) NIL;
storage.requestDataSize = 0;
storage.replyParamPtr = (Address) NIL;
storage.replyParamSize = 0;
storage.replyDataPtr = (Address) NIL;
storage.replyDataSize = 0;
/*
* Put the client onto the list of outstanding cache consistency
* messages.
*/
msgPtr = (ConsistMsgInfo *) malloc(sizeof(ConsistMsgInfo));
msgPtr->clientID = clientPtr->clientID;
msgPtr->flags = consistRpc.flags;
List_Insert((List_Links *) msgPtr, LIST_ATREAR(&consistPtr->msgList));
/*
* Have to release this monitor during the call-back so that
* an unrelated close can complete its call to Fsconsist_Close.
* Alternatives are to fix the consistency call-back RPC stubs
* so they don't lock the handle.
*/
if ((consistPtr->flags & FS_CONSIST_IN_PROGRESS) == 0) {
panic( "Client CallBack - consist flag not set\n");
}
UNLOCK_MONITOR;
numRefusals = 0;
while ( TRUE ) {
/*
* Send the rpc to the client. The client will return FAILURE if the
* open that we are talking about hasn't returned to the client yet.
*/
status = Rpc_Call(clientPtr->clientID, RPC_FS_CONSIST, &storage);
if (status != FAILURE) {
break;
} else {
numRefusals++;
if (numRefusals > 30) {
printf("Client %d dropped 30 %s requests for \"%s\" <%d,%d>\n",
clientPtr->clientID, ConsistType(flags),
Fsutil_HandleName(consistPtr->hdrPtr),
consistRpc.fileID.major, consistRpc.fileID.minor);
numRefusals = 0;
}
}
}
if (status != SUCCESS) {
/*
* Couldn't post call-back to the client.
*/
int ref, write, exec;
int clientID = clientPtr->clientID;
printf("ClientCommand, %s msg to client %d file \"%s\" <%d,%d> failed %x\n",
ConsistType(flags), clientID, Fsutil_HandleName(consistPtr->hdrPtr),
consistRpc.fileID.major, consistRpc.fileID.minor, status);
if (status == RPC_TIMEOUT || status == FS_STALE_HANDLE) {
/*
* If its really down, then nuke it from the
* list of clients using the file.
*/
Fsconsist_Kill(consistPtr, clientPtr->clientID, &ref, &write, &exec);
printf("\tClient state killed: %d refs %d write %d exec\n",
ref, write, exec);
} else {
/*
* Just nuke the message from the list so EndConsistency
* terminates. Sometimes the callback fails because the
* host is still booting and hasn't enabled its RPC service yet.
*/
List_Remove((List_Links *)msgPtr);
free((Address)msgPtr);
}
}
LOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* Fsconsist_RpcConsist --
*
* Service stub for RPC_FS_CONSIST. This is executed on a filesystem
* client in response to a cache consistency command. This schedules
* a call to ProcessConsist and returns a reply to the server.
* If, by remote chance, the message concerns an in-progress open
* that hasn't yet completed, we just return an error code to the
* server so that it will re-send its request after we know about
* the open.
*
* Results:
* SUCCESS.
*
* Side effects:
* Add work to the queue for the client consistency process.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
Fsconsist_RpcConsist(srvToken, clientID, command, storagePtr)
ClientData srvToken; /* Handle on server process passed to
* Rpc_Reply */
int clientID; /* ID of server controlling the file */
int command; /* Command identifier */
Rpc_Storage *storagePtr; /* The request fields refer to the request
* buffers and also indicate the exact amount
* of data in the request buffers. The reply
* fields are initialized to NIL for the
* pointers and 0 for the lengths. This can
* be passed to Rpc_Reply */
{
register ConsistMsg *consistArgPtr;
register ConsistItem *consistPtr;
register Fsrmt_FileIOHandle *rmtHandlePtr;
register ReturnStatus status;
consistArgPtr = (ConsistMsg *)storagePtr->requestParamPtr;
if (consistArgPtr->fileID.type != FSIO_RMT_FILE_STREAM) {
printf("Fsconsist_RpcConsist bad fileID <%d,%d,%d,%d> from client %d\n",
consistArgPtr->fileID.type, consistArgPtr->fileID.serverID,
consistArgPtr->fileID.major, consistArgPtr->fileID.minor,
clientID);
return(FAILURE);
}
/*
* This fetch locks the handle. In earlier versions of the kernel
* this could cause deadlock. This may still be true. 3/9/88.
*/
rmtHandlePtr = Fsutil_HandleFetchType(Fsrmt_FileIOHandle,
&consistArgPtr->fileID);
if (rmtHandlePtr == (Fsrmt_FileIOHandle *)NIL) {
if (Fsprefix_OpenInProgress(&consistArgPtr->fileID) == 0) {
status = FS_STALE_HANDLE;
} else {
/*
* A consistency message has arrived from an open from which
* we haven't received the reply. Return FAILURE to force
* the server to resend and give the open reply a chance
* of getting to us. This is the "open/consistency race".
*/
status = FAILURE;
}
} else if (rmtHandlePtr->openTimeStamp != consistArgPtr->openTimeStamp) {
if ((rmtHandlePtr->cacheInfo.version == consistArgPtr->version) &&
((consistArgPtr->flags & (FSCONSIST_DELETE_FILE|
FSCONSIST_WRITE_BACK_BLOCKS)) == 0)) {
/*
* We have the same version as the server, but there has been
* more open traffic than we realize. If this is any command
* except a write-back or delete (like return-attrs), then
* we'll do it.
*/
status = SUCCESS;
} else if (Fsprefix_OpenInProgress(&consistArgPtr->fileID) == 0) {
status = FS_STALE_HANDLE;
printf("Fsconsist_RpcConsist: <%d,%d> %s msg from %d timestamp %d not %d\n\t version %d and %d, returning stale handle\n",
consistArgPtr->fileID.major,
consistArgPtr->fileID.minor,
ConsistType(consistArgPtr->flags),
consistArgPtr->fileID.serverID,
consistArgPtr->openTimeStamp, rmtHandlePtr->openTimeStamp,
consistArgPtr->version, rmtHandlePtr->cacheInfo.version);
} else {
/*
* Timestamp mis-match and an open in progress.
* Possible open/consistency race.
*/
status = FAILURE;
}
} else {
status = SUCCESS;
}
if (rmtHandlePtr != (Fsrmt_FileIOHandle *)NIL) {
Fsutil_HandleRelease(rmtHandlePtr, TRUE);
}
if (status == SUCCESS) {
/*
* This is a message corresponding to our current notion of the
* file. Pass the message to a consistency handler process.
*/
consistPtr = (ConsistItem *) malloc(sizeof(ConsistItem));
consistPtr->serverID = clientID;
consistPtr->args = *consistArgPtr;
Proc_CallFunc(ProcessConsist, (ClientData) consistPtr, 0);
}
Rpc_Reply(srvToken, status, storagePtr, (int(*)())NIL, (ClientData)NIL);
return(SUCCESS);
}
/*
* ----------------------------------------------------------------------------
*
* ProcessConsist --
*
* Process a cache consistency request from the server. This
* is called in the background as the result of a request from
* the server. This routine is a thin wrapper around a routine
* in the cache module that does all the work.
*
* Results:
* None.
*
* Side effects:
* See Fscache_Consist.
*
* ----------------------------------------------------------------------------
*
*/
void
ProcessConsist(data, callInfoPtr)
ClientData data;
Proc_CallInfo *callInfoPtr;
{
register Fsrmt_FileIOHandle *handlePtr;
ReturnStatus status;
Rpc_Storage storage;
ConsistReply reply;
register ConsistItem *consistPtr;
consistPtr = (ConsistItem *) data;
callInfoPtr->interval = 0;
handlePtr = Fsutil_HandleFetchType(Fsrmt_FileIOHandle,
&consistPtr->args.fileID);
if (handlePtr == (Fsrmt_FileIOHandle *)NIL) {
printf("ProcessConsist: no handle <%d, %d, %d, %d>\n",
consistPtr->args.fileID.type,
consistPtr->args.fileID.serverID,
consistPtr->args.fileID.major,
consistPtr->args.fileID.minor);
free((char *) consistPtr);
return;
}
Fsutil_HandleUnlock(handlePtr);
FSCACHE_DEBUG_PRINT2("ProcessConsist: Got %s request for file %d\n",
ConsistType(consistPtr->args.flags), handlePtr->rmt.hdr.fileID.minor);
/*
* Process the request under the per file cache lock.
*/
reply.status = Fscache_Consist(&handlePtr->cacheInfo,
consistPtr->args.flags, &reply.cachedAttr);
reply.fileID = handlePtr->rmt.hdr.fileID;
reply.fileID.type = FSIO_LCL_FILE_STREAM;
Fsutil_HandleRelease(handlePtr, FALSE);
/*
* Set up the reply buffer.
*/
storage.requestParamPtr = (Address)&reply;
storage.requestParamSize = sizeof(ConsistReply);
storage.requestDataPtr = (Address)NIL;
storage.requestDataSize = 0;
storage.replyParamPtr = (Address)NIL;
storage.replyParamSize = 0;
storage.replyDataPtr = (Address)NIL;
storage.replyDataSize = 0;
for ( ; ; ) {
status = Rpc_Call(consistPtr->serverID, RPC_FS_CONSIST_REPLY, &storage);
if (status != SUCCESS) {
printf("Got error (%x) from consist reply on <%d,%d>\n", status,
reply.fileID.major, reply.fileID.minor);
}
if (status != RPC_TIMEOUT) {
break;
}
}
free((Address)consistPtr);
}
/*
*----------------------------------------------------------------------
*
* Fsconsist_RpcConsistReply --
*
* Service stub for RPC_FS_CONSIST_REPLY. This RPC indicates that
* the client has completed the consistency actions we previously
* asked it to perform.
*
* Results:
* SUCCESS, unless we don't like the fileID specified by the client.
*
* Side effects:
* See ProcessConsistReply.
*
*----------------------------------------------------------------------
*/
/*ARGSUSED*/
ReturnStatus
Fsconsist_RpcConsistReply(srvToken, clientID, command, storagePtr)
ClientData srvToken; /* Handle on server process passed to
* Rpc_Reply */
int clientID; /* Sprite ID of client host */
int command; /* Command identifier */
Rpc_Storage *storagePtr; /* The request fields refer to the request
* buffers and also indicate the exact amount
* of data in the request buffers. The reply
* fields are initialized to NIL for the
* pointers and 0 for the lengths. This can
* be passed to Rpc_Reply */
{
Fsio_FileIOHandle *handlePtr;
ConsistReply *replyPtr;
replyPtr = (ConsistReply *) storagePtr->requestParamPtr;
if (replyPtr->fileID.type != FSIO_LCL_FILE_STREAM) {
printf("Fsconsist_RpcConsistReply: bad fileID <%d,%d,%d,%d> from client %d\n",
replyPtr->fileID.type, replyPtr->fileID.serverID,
replyPtr->fileID.major, replyPtr->fileID.minor, clientID);
return(GEN_INVALID_ARG);
}
handlePtr = Fsutil_HandleFetchType(Fsio_FileIOHandle, &(replyPtr->fileID));
if (handlePtr == (Fsio_FileIOHandle *) NIL) {
printf("Fsconsist_RpcConsistReply: no handle <%d,%d> for client %d\n",
replyPtr->fileID.major, replyPtr->fileID.minor, clientID);
return(FS_STALE_HANDLE);
}
/*
* Unlock the handle to prevent deadlock if this RPC
* arrives as ClientRemoveCallback is attempting to relock
* the handle on its way out of the per-file consistency monitor.
* (This can only happen if the server 'kills' the client state and so
* aborts the cache consistency wait before this arrives.)
* Ordinarily this RPC arrives as it is waiting with the monitor
* unlocked. The handle lock is not needed anyway because once we have
* successfully fetched the handle it won't go away on us. Furthermore,
* ProcessConsistReply can handle it if the client state has been killed.
*/
Fsutil_HandleUnlock(handlePtr);
ProcessConsistReply(&handlePtr->consist, clientID, replyPtr);
Fsutil_HandleRelease(handlePtr, FALSE);
Rpc_Reply(srvToken, SUCCESS, storagePtr,
(int (*)())NIL, (ClientData)NIL);
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* ProcessConsistReply --
*
* Process the reply sent by the client for the given handle.
* This updates the caching state of the client, and it may
* also update the handle attributes based on information returned
* from the client. This is call after the client has completed
* the cache consistency actions we previously requested of it.
*
* Results:
* None.
*
* Side effects:
* Element deleted from the list of outstanding client consist
* messages. Also if the message was for invalidation then the
* client list entry is marked as non-cacheable. IMPORTANT:
* client list entry may be REMOVED here which prevents safe
* use of LIST_FORALL iteration over the client list by any
* routine that calls ClientCommand (which eventually gets us called)
*
*----------------------------------------------------------------------
*/
ENTRY void
ProcessConsistReply(consistPtr, clientID, replyPtr)
Fsconsist_Info *consistPtr; /* File to process reply for*/
int clientID; /* Client who sent us the
* reply. */
register ConsistReply *replyPtr; /* The reply that was sent. */
{
register ConsistMsgInfo *msgPtr;
register Fsconsist_ClientInfo *clientPtr;
Boolean found = FALSE;
Fsio_FileIOHandle *handlePtr;
LOCK_MONITOR;
/*
* Find the client in the list of pending messages. Notify the
* opening process after all the clients have responded.
*/
LIST_FORALL(&(consistPtr->msgList), (List_Links *) msgPtr) {
if (msgPtr->clientID == clientID) {
List_Remove((List_Links *) msgPtr);
found = TRUE;
break;
}
}
if (List_IsEmpty(&(consistPtr->msgList))) {
Sync_Broadcast(&consistPtr->repliesIn);
}
if (!found) {
/*
* We don't know about this cache consistency action by the
* client, perhaps because we concluded it was down and
* killed our state about it. It is also possible that it is an
* old message from the client, probably queued in the network
* interface across a reboot or from a gateway.
*/
UNLOCK_MONITOR;
return;
}
if (replyPtr->status != SUCCESS) {
printf("ProcessConsist: %s request failed <%x> file \"%s\" <%d,%d>\n",
ConsistType(msgPtr->flags), replyPtr->status,
Fsutil_HandleName(consistPtr->hdrPtr),
consistPtr->hdrPtr->fileID.major,
consistPtr->hdrPtr->fileID.minor);
consistPtr->flags |= FS_CONSIST_ERROR;
} else {
if ((msgPtr->flags & FSCONSIST_WRITE_BACK_BLOCKS) &&
(consistPtr->lastWriter == clientID)) {
/*
* We just got the most recent blocks so we don't care who the
* last writer is anymore.
*/
consistPtr->lastWriter = -1;
}
/*
* Look for client list entry that match the client, and update
* its state to reflect the consistency action by the client.
* Because we've release the monitor lock during the previous
* call-back, an unrelated action may have removed the client
* from the list - no big deal.
*/
LIST_FORALL(&(consistPtr->clientList), (List_Links *) clientPtr) {
if (clientPtr->clientID == clientID) {
Fsrecov_HandleState recovInfo;
ReturnStatus status;
handlePtr = (Fsio_FileIOHandle *)consistPtr->hdrPtr;
if (msgPtr->flags & (FSCONSIST_WRITE_BACK_BLOCKS |
FSCONSIST_WRITE_BACK_ATTRS)) {
Fscache_UpdateAttrFromClient(clientID,
&handlePtr->cacheInfo, &replyPtr->cachedAttr);
(void) Fsdm_UpdateDescAttr(handlePtr,
&handlePtr->cacheInfo.attr, -1);
}
if (clientPtr->use.ref == 0 &&
consistPtr->lastWriter != clientID) {
/*
* If the handle had a 0 ref count but was still in the
* recovery box due to having dirty blocks in the client's
* cache, we can now get rid of it. XXX I hope this is
* the right place to do it. Should it be done
* above? Leaving it here could get more clients,
* but if they aren't the last writer and they have 0 refs,
* they should go away.
*/
if (recov_Transparent &&
Fsrecov_GetHandle(consistPtr->hdrPtr->fileID,
clientID, &recovInfo, FALSE) == SUCCESS) {
if (recovInfo.use.ref <= 0) {
status = Fsrecov_DeleteHandle(consistPtr->hdrPtr,
clientID, FS_WRITE | FS_LAST_DIRTY_BLOCK);
if (status != SUCCESS) {
/* Should I panic? */
printf("ProcessConsistReply: couldn't ");
printf("delete handle from recov box.\n");
}
} else {
printf("ProcessConsistReply: ref count ");
panic("on object too high.");
}
}
REMOVE_CLIENT(clientPtr);
} else if (msgPtr->flags & FSCONSIST_INVALIDATE_BLOCKS) {
clientPtr->cached = FALSE;
/* Change cacheability info in recov box. */
if (recov_Transparent &&
Fsrecov_GetHandle(consistPtr->hdrPtr->fileID,
clientID, &recovInfo, FALSE) == SUCCESS) {
if (recovInfo.clientData != FALSE) {
recovInfo.clientData = FALSE;
if (Fsrecov_UpdateHandle(consistPtr->hdrPtr->fileID,
clientID, &recovInfo) != SUCCESS) {
printf(" ProcessConsistReply: ");
printf("Couldn't update recov box.\n");
}
}
}
}
break;
}
}
}
free((Address) msgPtr);
UNLOCK_MONITOR;
}
/*
*----------------------------------------------------------------------
*
* ConsistType --
*
* Utility routine to map from consistency flags to a printable string.
*
* Results:
* A pointer to a printable string.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
char *
ConsistType(flags)
int flags; /* Cache consistency message flags */
{
register char *result;
switch (flags) {
case FSCONSIST_WRITE_BACK_BLOCKS:
result = "write-back";
break;
case FSCONSIST_INVALIDATE_BLOCKS:
result = "invalidate";
break;
case (FSCONSIST_WRITE_BACK_BLOCKS|FSCONSIST_INVALIDATE_BLOCKS):
result = "write-back & invalidate";
break;
case FSCONSIST_DELETE_FILE:
result = "delete";
break;
case FSCONSIST_WRITE_BACK_ATTRS:
result = "return-attrs";
break;
default:
result = "UNKNOWN";
break;
}
return result;
}
/*
* ----------------------------------------------------------------------------
*
* Fsconsist_UpdateFileConsistencyList --
*
* This updates the data structures to include this file in the
* client consistency lists.
*
* Results:
* None.
*
* Side effects:
* Lists updated, lastWriter field set, etc.
*
* ----------------------------------------------------------------------------
*
*/
ENTRY void
Fsconsist_UpdateFileConsistencyList(handlePtr, clientID, useFlags, cacheable)
Fsio_FileIOHandle *handlePtr; /* File to check consistency of. */
int clientID; /* ID of the host doing the open */
register int useFlags; /* useFlags from the open call */
Boolean cacheable; /* TRUE if file is cacheable. */
{
register Fsconsist_Info *consistPtr = &handlePtr->consist;
LOCK_MONITOR;
/*
* Add ourselves to the list of clients using the file.
*/
UpdateList(consistPtr, clientID, useFlags, cacheable, (int *) NIL);
UNLOCK_MONITOR;
return;
}